package com.kingmed.kmss.bpmn.flow.service.custom;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.kingmed.kmss.bpmn.flow.helper.HistoryHelper;
import com.kingmed.kmss.bpmn.flow.helper.RepositoryHelper;
import com.kingmed.kmss.bpmn.modules.backup.model.BizApprovalStatus;
import com.kingmed.kmss.bpmn.modules.backup.model.BizApprovalTrail;
import com.kingmed.kmss.bpmn.modules.backup.service.BizApprovalStatusService;
import com.kingmed.kmss.bpmn.modules.backup.service.BizApprovalTrailService;
import com.kingmed.kmss.common.bo.bpmn.FlowNodeBO;
import com.kingmed.kmss.common.bo.bpmn.ProcessInstanceStatusBO;
import com.kingmed.kmss.common.constant.bpmn.BpmnConstant;
import com.kingmed.kmss.common.constant.common.OperatorConstants;
import com.kingmed.kmss.common.dto.admin.ums.AdminDTO;
import com.kingmed.kmss.framework.feign.client.admin.CommonAdminFeignServiceFacade;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.common.engine.impl.el.VariableContainerWrapper;
import org.flowable.engine.ManagementService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author liangzhi
 * @date 2023/10/11
 */
@Slf4j
@Service
public class ProcessInstanceServiceImpl implements ProcessInstanceService {
    @Autowired
    private RepositoryHelper repositoryHelper;
    @Autowired
    private HistoryHelper historyHelper;
    @Autowired
    ManagementService managementService;
    @Autowired
    ProcessEngineConfigurationImpl processEngineConfiguration;
    @Autowired
    private CommonAdminFeignServiceFacade adminFeignServiceFacade;
    @Autowired
    private BizApprovalStatusService bizApprovalStatusService;
    @Autowired
    private BizApprovalTrailService bizApprovalTrailService;

    @Override
    public List<FlowNodeBO> listFlowNode(String processInstanceId) {
        // 获取流程实例
        HistoricProcessInstance historicProcessInstance = historyHelper.findHistoryByProcessIdWithVariables(processInstanceId);
        Map<String, Object> variablesMap = historicProcessInstance.getProcessVariables();
        variablesMap.put("approved", true);
        // 获取流程定义
        BpmnModel bpmnModel = repositoryHelper.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
        // 获取流程定义里面的节点
        Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
        // 查找所有审批路径
        List<FlowElement> passElements = this.searchElement(flowElements, variablesMap);
        // 转换成节点列表
        List<FlowNodeBO> flowNodeBOList = this.convertFlowNodeBO(passElements, variablesMap, processInstanceId);
        // 标记已经走过的审批流
        this.markHandledFlowNode(processInstanceId, flowNodeBOList);
        return flowNodeBOList;
    }

    @Override
    public List<ProcessInstanceStatusBO> listProcessInstanceStatusBO(String businessId, String businessType) {
        // 获取发起审批次数，按时间倒排
        List<BizApprovalStatus> bizApprovalStatusList = bizApprovalStatusService.listByBusinessId(businessId, businessType);
        List<ProcessInstanceStatusBO> processInstanceStatusBOList = new ArrayList<>();
        for (BizApprovalStatus bizApprovalStatus : bizApprovalStatusList) {
            List<FlowNodeBO> flowNodeBOList = this.listFlowNode(bizApprovalStatus.getProcInstId());
            ProcessInstanceStatusBO processInstanceStatusBO = new ProcessInstanceStatusBO();
            processInstanceStatusBO.setProcessInstanceId(bizApprovalStatus.getProcInstId());
            processInstanceStatusBO.setProcessStatus(bizApprovalStatus.getCurrStatus());
            processInstanceStatusBO.setFlowNodeBOList(flowNodeBOList);
            processInstanceStatusBOList.add(processInstanceStatusBO);
        }
        return processInstanceStatusBOList;
    }

    private void markHandledFlowNode(String processInstanceId, List<FlowNodeBO> flowNodeBOList) {
        List<HistoricTaskInstance> historicTaskList = historyHelper.findHistoricTaskList(processInstanceId);
        if (CollectionUtils.isEmpty(historicTaskList)) {
            return;
        }
        historicTaskList.forEach(historicTaskInstance -> {
            flowNodeBOList.forEach(flowNodeBO -> {
                if (StringUtils.equals(flowNodeBO.getNodeId(), historicTaskInstance.getTaskDefinitionKey())) {
                    flowNodeBO.setStatus(historicTaskInstance.getEndTime() == null ? 1 : 2);
                }
            });
        });
    }

    private List<FlowNodeBO> convertFlowNodeBO(List<FlowElement> passElements, Map<String, Object> variablesMap, String processInstanceId) {
        BizApprovalStatus bizApprovalStatus = bizApprovalStatusService.getByProcessInstanceId(processInstanceId);
        List<BizApprovalTrail> bizApprovalTrailList = bizApprovalTrailService.listByProcessInstanceId(processInstanceId);
        List<FlowNodeBO> flowNodeBOList = new ArrayList<>(passElements.size() + 1);
        int i = 1;
        // 初始化“提交审批”节点
        FlowNodeBO startFlowNode = new FlowNodeBO();
        startFlowNode.setNodeId("");
        startFlowNode.setNodeName("提交审批");
        startFlowNode.setAssigneeNames(bizApprovalTrailList.get(0).getApprovedByName());
        startFlowNode.setAssignees(bizApprovalTrailList.get(0).getApprovedBy());
        startFlowNode.setSeqNum(i++);
        startFlowNode.setStatus(2);
        flowNodeBOList.add(startFlowNode);
        // 拼接节点
        for (int j = 0, len = passElements.size(); j < len; j++) {
            FlowElement flowElement = passElements.get(j);
            FlowNodeBO flowNodeBO = new FlowNodeBO();
            flowNodeBO.setNodeId(flowElement.getId());
            flowNodeBO.setNodeName(flowElement.getName());
            flowNodeBO.setSeqNum(i++);
            flowNodeBO.setStatus(0);
            // 赋值审批人(先拿审批记录里面的)
            if (j < bizApprovalTrailList.size() - 1) {
                flowNodeBO.setAssignees(bizApprovalTrailList.get(j + 1).getApprovedBy());
                flowNodeBO.setAssigneeNames(bizApprovalTrailList.get(j + 1).getApprovedByName());
            } else if (j == bizApprovalTrailList.size() - 1) {
                flowNodeBO.setAssignees(bizApprovalStatus.getNextApprover());
                flowNodeBO.setAssigneeNames(bizApprovalStatus.getNextApproverName());
            } else {
                List<String> assignees = this.listAssignees(flowElement, variablesMap);
                if (CollectionUtils.isNotEmpty(assignees)) {
                    List<AdminDTO> adminDTOS = this.listAssigneeAdminInfo(assignees);
                    flowNodeBO.setAssignees(adminDTOS.stream().map(AdminDTO::getUsername).collect(Collectors.joining(",")));
                    flowNodeBO.setAssigneeNames(adminDTOS.stream().map(AdminDTO::getNickName).collect(Collectors.joining(",")));
                } else {
                    flowNodeBO.setAssigneeNames("无");
                }
            }
            flowNodeBOList.add(flowNodeBO);
        }
        return flowNodeBOList;
    }

    private List<String> listAssignees(FlowElement flowElement, Map<String, Object> variablesMap) {
        List<String> assigneeList = new ArrayList<>();
        UserTask userTask = (UserTask) flowElement;
        if (StringUtils.isNotBlank(userTask.getAssignee())) {
            // 直接分配
            if (!StringUtils.contains(userTask.getAssignee(), "${")) {
                assigneeList.addAll(Arrays.asList(userTask.getAssignee().split(OperatorConstants.A_SEMICOLON_KEY)));
            } else {
                // 表达式方式
                Expression expression = processEngineConfiguration.getExpressionManager().createExpression(userTask.getAssignee());
                VariableContainerWrapper variableContainerWrapper = new VariableContainerWrapper(variablesMap);
                Object value = expression.getValue(variableContainerWrapper);
                if (ObjectUtil.isNotNull(value)) {
                    assigneeList.addAll(Arrays.asList(value.toString().split(OperatorConstants.A_SEMICOLON_KEY)));
                } else {
                    log.warn("获取审批用户失败，表达式：{}", userTask.getAssignee());
                }
            }
        }
        // 用户组
        if (CollectionUtils.isNotEmpty(userTask.getCandidateGroups())) {
            for (String candidateGroup : userTask.getCandidateGroups()) {
                Integer orgType = (Integer) variablesMap.get(BpmnConstant.VarKeys.ORG_TYPE);
                Long parentOrgId = (Long) variablesMap.get(BpmnConstant.VarKeys.PARENT_ORG_ID);
                List<FlowableListener> taskListeners = userTask.getTaskListeners();
                Optional<FlowableListener> directListenerOpt = taskListeners.stream()
                        .filter(flowableListener -> StringUtils.equals(flowableListener.getEvent(), "create") && flowableListener.getImplementation().contains("DirectAssignUserTaskListener"))
                        .findFirst();
                if (directListenerOpt.isPresent()) {
                    List<String> candidateUsers = adminFeignServiceFacade.getRoleUsers(candidateGroup, orgType, String.valueOf(parentOrgId));
                    assigneeList.addAll(candidateUsers);
                } else {
                    List<String> candidateUsers = adminFeignServiceFacade.getRoleUsersByOrg(candidateGroup, orgType, String.valueOf(parentOrgId));
                    assigneeList.addAll(candidateUsers);
                }
            }
        }
        if (CollectionUtils.isEmpty(assigneeList)) {
            return Collections.emptyList();
        }
        return assigneeList;
    }

    private List<AdminDTO> listAssigneeAdminInfo(List<String> assigneeList) {
        List<AdminDTO> adminDTOS = adminFeignServiceFacade.listByUsername(assigneeList);
        if (adminDTOS.size() != assigneeList.size()) {
            List<String> existEmpNum = adminDTOS.stream().map(AdminDTO::getEmployeeNum).collect(Collectors.toList());
            log.error("员工信息不对称，工号：{},已获取工号：{}", StringUtils.join(assigneeList, ","), StringUtils.join(existEmpNum, ","));
        }
        return adminDTOS;
    }

    private List<FlowElement> searchElement(Collection<FlowElement> flowElements, Map<String, Object> variablesMap) {
        List<FlowElement> passElements = new ArrayList<>();
        // 从开始节点开始往下找
        flowElements.stream()
                .filter(StartEvent.class::isInstance)
                .findFirst()
                .ifPresent(startElement -> {
                    List<SequenceFlow> outgoingFlows = ((StartEvent) startElement).getOutgoingFlows();
                    // 开始节点只有一条箭头
                    String targetRef = outgoingFlows.get(0).getTargetRef();
                    FlowElement targetElement = this.getFlowElement(flowElements, targetRef);
                    // 添加有效节点
                    this.addPassElementList(passElements, flowElements, targetElement, variablesMap);
                });
        return passElements;
    }

    /**
     * 添加经过的路径元素
     */
    private void addPassElementList(List<FlowElement> passElements, Collection<FlowElement> flowElements, FlowElement curFlowElement, Map<String, Object> variablesMap) {
        // 任务节点
        if (curFlowElement instanceof UserTask) {
            this.handleUserTaskElement(passElements, flowElements, curFlowElement, variablesMap);
            return;
        }
        // 排他网关
        if (curFlowElement instanceof ExclusiveGateway) {
            this.handleExclusiveGateway(passElements, flowElements, curFlowElement, variablesMap);
            return;
        }
        // 并行网关
        if (curFlowElement instanceof ParallelGateway) {
            this.handleParallelGateway(passElements, flowElements, curFlowElement, variablesMap);
        }
    }


    private void handleUserTaskElement(List<FlowElement> passElements, Collection<FlowElement> flowElements, FlowElement curFlowElement, Map<String, Object> variablesMap) {
        passElements.add(curFlowElement);
        List<SequenceFlow> outgoingFlows = ((UserTask) curFlowElement).getOutgoingFlows();
        String targetRef = outgoingFlows.get(0).getTargetRef();
        if (outgoingFlows.size() > 1) {
            // 找到表达式成立的sequenceFlow
            SequenceFlow sequenceFlow = this.getSequenceFlow(variablesMap, outgoingFlows);
            targetRef = sequenceFlow.getTargetRef();
        }
        FlowElement targetElement = getFlowElement(flowElements, targetRef);
        this.addPassElementList(passElements, flowElements, targetElement, variablesMap);
    }

    private void handleExclusiveGateway(List<FlowElement> passElements, Collection<FlowElement> flowElements, FlowElement curFlowElement, Map<String, Object> variablesMap) {
        // 获取符合条件的sequenceFlow的目标FlowElement
        List<SequenceFlow> exclusiveGatewayOutgoingFlows = ((ExclusiveGateway) curFlowElement).getOutgoingFlows();
        // 找到表达式成立的sequenceFlow
        SequenceFlow sequenceFlow = getSequenceFlow(variablesMap, exclusiveGatewayOutgoingFlows);
        // 根据ID找到FlowElement
        FlowElement targetElement = getFlowElement(flowElements, sequenceFlow.getTargetRef());
        this.addPassElementList(passElements, flowElements, targetElement, variablesMap);
    }

    private void handleParallelGateway(List<FlowElement> passElements, Collection<FlowElement> flowElements, FlowElement curFlowElement, Map<String, Object> variablesMap) {
        FlowElement targetElement;
        List<SequenceFlow> parallelGatewayOutgoingFlows = ((ParallelGateway) curFlowElement).getOutgoingFlows();
        for (SequenceFlow sequenceFlow : parallelGatewayOutgoingFlows) {
            targetElement = getFlowElement(flowElements, sequenceFlow.getTargetRef());
            this.addPassElementList(passElements, flowElements, targetElement, variablesMap);
        }
    }


    /**
     * 获取表达式成立的顺序流
     */
    private SequenceFlow getSequenceFlow(Map<String, Object> variablesMap, List<SequenceFlow> outgoingFlows) {
        return outgoingFlows.stream().filter(item -> {
                    try {
                        return this.getElValue(item.getConditionExpression(), variablesMap);
                    } catch (Exception e) {
                        log.error("表达式判断异常", e);
                        return false;
                    }
                }).findFirst()
                .orElse(outgoingFlows.get(0));
    }

    private boolean getElValue(String exp, Map<String, Object> variablesMap) {
        Expression expression = processEngineConfiguration.getExpressionManager().createExpression(exp);
        VariableContainerWrapper variableContainerWrapper = new VariableContainerWrapper(variablesMap);
        Object value = expression.getValue(variableContainerWrapper);
        return value != null && "true".equals(value.toString());
    }


    private FlowElement getFlowElement(Collection<FlowElement> flowElements, String targetRef) {
        return flowElements.stream().filter(flowElement -> targetRef.equals(flowElement.getId())).findFirst().orElse(null);
    }
}
